#
# A parser for ASC files.
#
# Auther: Wesley Liu
# Email: jinvy.liu@gmail.com
# Date: 12/19/2007
#

require 'matrix'
require File.dirname(__FILE__)+'/IFileParser'
require File.dirname(__FILE__)+'/../Scene/ListScene'
require File.dirname(__FILE__)+'/../Scene/Mesh'
require File.dirname(__FILE__)+'/../Scene/Material'

class ASCParser < IFileParser
    def initialize
    end
    
    def for
        'ase'
    end
    
    def parse filename, progressTarget
        scene = ListScene.new
        stack = Array.new
        materials = Array.new
        filesize = File.size(filename).to_f
        increase = 0.0
        File.open(filename).each do |line|
            # Update the progress bar
            increase += (line.size / filesize * 100)
            if increase > 1
                progressTarget.value += increase.to_i
                increase -= increase.to_i
            end
            # Parse the file data
            if /\s*\}/ =~ line
                stack.pop
                next
            end
            case stack.last
                when nil
                    if /\*GEOMOBJECT/ =~ line
                        stack.push '*GEOMOBJECT'
                        next
                    end
                    if /\*MATERIAL_LIST/ =~ line
                        stack.push '*MATERIAL_LIST'
                        next
                    end

                when '*GEOMOBJECT'
                    if /\*NODE_NAME\s+"(\S+)"/ =~ line
                        @currentmesh = Mesh.new
                        scene.addmesh @currentmesh
                        @currentmesh.name = $1
                        next
                    end
                    if /\*NODE_TM/ =~ line
                        stack.push '*NODE_TM'
                        next
                    end
                    if /\*MESH/ =~ line
                        stack.push '*MESH'
                        next
                    end
                    if /\*MATERIAL_REF\s+(\S+)/ =~ line
                        @currentmesh.material = materials[$1.to_i]
                        next
                    end
                    if /\*INSTANCED_MESH\s+"(\S+)"/ =~ line
                        instance = findInstance scene, $1
                        if instance == nil
                            raise "can not find instance #{$1}"
                        end
                        @currentmesh.instance instance
                        next
                    end
                    
                when '*NODE_TM'
                    # handle the transform
                    next
                    
                when '*MESH'
                    if /\**MESH_VERTEX_LIST/ =~ line
                        stack.push '*MESH_VERTEX_LIST'
                        next
                    end
                    if /\*MESH_FACE_LIST/ =~ line
                        stack.push '*MESH_FACE_LIST'
                        next
                    end

                when '*MESH_VERTEX_LIST'
                    if /\*MESH_VERTEX\s+\d+\s+(\S+)\s+(\S+)\s+(\S+)/ =~ line
                        i = @currentmesh.vertices.length
                        @currentmesh.vertices[i] = Vector[$1.to_f, $2.to_f, $3.to_f]
                        next
                    end

                when '*MESH_FACE_LIST'
                    if /\*MESH_FACE.+A:\s+(\d+)\s+B:\s+(\d+)\s+C:\s+(\d+)\s+/ =~ line
                        i = @currentmesh.faces.length
                        @currentmesh.faces[i] = Vector[$1.to_i, $2.to_i, $3.to_i]
                        next
                    end
                
                when '*MATERIAL_LIST'
                    if /\*MATERIAL\s+(\S+)\s+\{/ =~ line
                        @currentmaterial = Material.new
                        materials[$1.to_i] = @currentmaterial
                        stack.push '*MATERIAL'
                    end
                    
                when '*MATERIAL'
                    if /\*MATERIAL_AMBIENT\s+(\S+)\s+(\S+)\s+(\S+)/ =~ line
                        @currentmaterial.ambient = [$1.to_f, $2.to_f, $3.to_f, 1.0]
                    end
                    if /\*MATERIAL_DIFFUSE\s+(\S+)\s+(\S+)\s+(\S+)/ =~ line
                        @currentmaterial.diffuse = [$1.to_f, $2.to_f, $3.to_f, 1.0]
                    end
                    if /\*MATERIAL_SPECULAR\s+(\S+)\s+(\S+)\s+(\S+)/ =~ line
                        @currentmaterial.specular = [$1.to_f, $2.to_f, $3.to_f, 1.0]
                    end
                    if /\*MATERIAL_SHINE\s+(\S+)/ =~ line
                        @currentmaterial.shininess = $1.to_f
                    end
                    if /\*MATERIAL_TRANSPARENCY\s+(\S+)/ =~ line
                        alpha = 1 - $1.to_f
                        @currentmaterial.ambient[3] = alpha 
                        @currentmaterial.diffuse[3] = alpha
                        @currentmaterial.specular[3] = alpha
                    end
                    if /\*SUBMATERIAL/ =~ line
                        stack.push '*SUBMATERIAL'
                    end
            end
        end
        scene
    end
    
    def findInstance scene, name
        scene.each do |mesh|
            if mesh.name == name
                return mesh
            end
        end
    end
end

if __FILE__ == $0
    require 'fox16'
    include Fox

    #test files: MultiMaterialBox, LightCameraTest, Part2-2-2-2-1
    scene = ASCParser.new.parse(File.dirname(__FILE__)+'/../../test/MultiMaterialBox.ASE', FXDataTarget.new(0))
    scene.each do |mesh|
        puts mesh.name
        puts mesh.vertices.length
        puts mesh.faces.length
        if mesh.material != nil
            puts mesh.material.ambient.to_s
            puts mesh.material.diffuse.to_s
            puts mesh.material.specular.to_s
            puts mesh.material.shininess.to_s
        end
    end
    puts 'root aabb:'
    scene.rootbb.aabb2v.each do |v|
        puts "#{v[0]}, #{v[1]}, #{v[2]}"
    end

end